#include "codeText.h"

lineNumberArea::lineNumberArea(codeText *editor): QWidget(editor){
	
	codeEditor = editor;

}

void lineNumberArea::paintEvent(QPaintEvent *event){
	
	codeEditor->lineNumberAreaPaintEvent(event);

}

QSize lineNumberArea::sizeHint() const{
	
	return QSize(codeEditor->lineNumberAreaWidth(), 0);

}

codeText::codeText(QWidget *parent) : QPlainTextEdit(parent){
    
	lineArea = new lineNumberArea(this);

	connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
	connect(this, SIGNAL(updateRequest(const QRect &, int)), this, SLOT(updateLineNumberArea(const QRect &, int)));
	connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));

	updateLineNumberAreaWidth(0);
	highlightCurrentLine();
	
}

int codeText::lineNumberAreaWidth(){
	
	int digits = 1;
	int max = qMax(1, blockCount());
	while (max >= 10) {
		max /= 10;
		++digits;
	}
	int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
	return space;

}

void codeText::updateLineNumberAreaWidth(int newBlockCount){
     
	setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);

}

void codeText::updateLineNumberArea(const QRect &rect, int dy){
	
	if(dy)
		lineArea->scroll(0, dy);
	else
		lineArea->update(0, rect.y(), lineArea->width(), rect.height());
	if(rect.contains(viewport()->rect()))
		updateLineNumberAreaWidth(0);
 
}

void codeText::resizeEvent(QResizeEvent *e){
	
	QPlainTextEdit::resizeEvent(e);

	QRect cr = contentsRect();
	lineArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
 
}

void codeText::highlighSpecificLine(int i){
	
	QList<QTextEdit::ExtraSelection> extraSelections;

	if (isReadOnly()) {
		QTextEdit::ExtraSelection selection;
		QColor lineColor = QColor(Qt::yellow).lighter(160);
		selection.format.setBackground(lineColor);
		selection.format.setProperty(QTextFormat::FullWidthSelection, true);
		selection.cursor = textCursor();
		selection.cursor.movePosition(QTextCursor::Start);
		selection.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, i - 1);
		selection.cursor.clearSelection();
		setTextCursor(selection.cursor);
		extraSelections.append(selection);
	}
	setExtraSelections(extraSelections);

}

void codeText::highlightCurrentLine(){
	
	QList<QTextEdit::ExtraSelection> extraSelections;

	if (!isReadOnly()) {
		QTextEdit::ExtraSelection selection;
		QColor lineColor = QColor(Qt::yellow).lighter(160);
		selection.format.setBackground(lineColor);
		selection.format.setProperty(QTextFormat::FullWidthSelection, true);
		selection.cursor = textCursor();
		selection.cursor.clearSelection();
		extraSelections.append(selection);
	}
	setExtraSelections(extraSelections);
 
}

void codeText::lineNumberAreaPaintEvent(QPaintEvent *event){
	
	QPainter painter(lineArea);
	painter.fillRect(event->rect(), Qt::lightGray);

	QTextBlock block = firstVisibleBlock();
	int blockNumber = block.blockNumber();
	int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
	int bottom = top + (int) blockBoundingRect(block).height();

	while (block.isValid() && top <= event->rect().bottom()) {
		if (block.isVisible() && bottom >= event->rect().top()) {
			QString number = QString::number(blockNumber + 1);
			painter.setPen(Qt::black);
			painter.drawText(0, top, lineArea->width(), fontMetrics().height(), Qt::AlignRight, number);
		}
		block = block.next();
		top = bottom;
		bottom = top + (int) blockBoundingRect(block).height();
		++blockNumber;
	}
	
}

codeEdit::codeEdit(QWidget *parent): QWidget(parent){

	vLayout = new QVBoxLayout(this);
	high = new highlighter;
	
	modified = false;
	saved = true;
	vLayout->setContentsMargins(0, 0, 0, 0);
	createActions();
	createToolBar();
	createCodeText();
	createStatusBar();
	
}

void codeEdit::createActions(){

	saveAct = new QAction(tr("Zapíše změny."), this);
	saveAct->setShortcut(tr("Ctrl-S"));
	saveAct->setIcon(QIcon(":img/tool-save.png"));
	connect(saveAct, SIGNAL(triggered()), this, SLOT(saveSlot()));

	copyAct = new QAction("Kopírovat", this);
	copyAct->setIcon(QIcon(":img/copy.png"));
	copyAct->setShortcuts(QKeySequence::Copy);
	copyAct->setToolTip(tr("Zkopíruje označený text."));
	
	cutAct = new QAction("Vyjmout", this);
	cutAct->setIcon(QIcon(":img/cut.png"));
	cutAct->setShortcuts(QKeySequence::Cut);
	cutAct->setToolTip(tr("Vyjme označený text."));
	
	undoAct = new QAction("Zpet", this);
	undoAct->setIcon(QIcon(":img/undo.png"));
	undoAct->setShortcut(tr("Ctrl-Z"));
	undoAct->setToolTip(tr("Vratí se o krok zpět."));
	
	redoAct = new QAction("Vpred", this);
	redoAct->setIcon(QIcon(":img/redo.png"));
	redoAct->setShortcut(tr("Ctrl-Y"));
	redoAct->setToolTip(tr("Vratí se o krok vpřed."));
	
	pasteAct = new QAction("Vložit", this);
	pasteAct->setIcon(QIcon(":img/paste.png"));
	pasteAct->setShortcuts(QKeySequence::Paste);
	pasteAct->setToolTip(tr("Vloží text z clipboardu."));
	
}

void codeEdit::createToolBar(){

	toolBar = new QToolBar(this);
	
	toolBar->addAction(saveAct);
	toolBar->addSeparator();
	toolBar->addAction(undoAct);
	toolBar->addAction(redoAct);
	toolBar->addSeparator();
	toolBar->addAction(copyAct);
	toolBar->addAction(cutAct);
	toolBar->addAction(pasteAct);
	vLayout->addWidget(toolBar, 0);

}

void codeEdit::documentWasModified(){
	
	statusChanged();
	saved = false;
	modified = true;

}

void codeEdit::createCodeText(){

	text = new codeText(this);
	
	vLayout->addWidget(text, 1);
	high->setDocument(text->document());
	connect(copyAct, SIGNAL(triggered()), text, SLOT(copy()));
	connect(cutAct, SIGNAL(triggered()), text, SLOT(cut()));
	connect(pasteAct, SIGNAL(triggered()), text, SLOT(paste()));
	connect(undoAct, SIGNAL(triggered()), text, SLOT(undo()));
	connect(redoAct, SIGNAL(triggered()), text, SLOT(redo()));
	connect(text, SIGNAL(textChanged()), this, SLOT(changed()));

}

void codeEdit::changed(){

	emit textChanged();

}

void codeEdit::statusChanged(){

	statusBar->showMessage(tr("Soubor '%1' byl pozměněn.").arg(file));

}

void codeEdit::setModified(bool boolean){

	if(boolean)
		statusChanged();
	modified = boolean;

}

bool codeEdit::isModified() const{

	return modified;

}

QString codeEdit::getFile() const{

	return file;

}

void codeEdit::setFile(const QString &fileName){

	if(isModified())
		return;
	
	QFile fd(fileName);
	file = fileName;

	if(fd.open(QFile::ReadOnly | QFile::Text)){
		text->setPlainText(fd.readAll());
		connect(text->document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified()));
	}
	else
		QMessageBox::warning(this, 
							tr("Chyba čtení souboru"),
							tr("Nelze přečíst soubor <b>%1</b>.").
								arg(file));

}

bool codeEdit::save(QString &fileName){

	QFile fd(fileName);
	
	if(fd.open(QFile::WriteOnly | QFile::Text)){
		QTextStream out(&fd);
		
		QApplication::setOverrideCursor(Qt::WaitCursor);
		out << text->toPlainText();
		QApplication::restoreOverrideCursor();
		saved = true;
		statusBar->showMessage(tr("Soubor '%1' byl uložen.").arg(fileName), 5000);
		return true;
	}
	else{
		QMessageBox::warning(this,
							tr("Chyba zápisu"), 
							tr("Nelze zapsat do souboru <b>%1</b>.")
								.arg(file));
		return false;
	}
							

}

void codeEdit::saveSlot(){

	save(file);

}

bool codeEdit::isSaved() const{
	
	return saved;
	
}

void codeEdit::createStatusBar(){

	statusBar = new QStatusBar;
	
	vLayout->addWidget(statusBar, 0);

}
